package ro.romanescu.loc4j.impl;

import java.net.MalformedURLException;
import java.net.URL;

import java.util.HashMap;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.XMLEvent;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.codehaus.stax2.XMLInputFactory2;
import org.codehaus.stax2.XMLStreamReader2;

import ro.romanescu.loc4j.Dictionary;
import ro.romanescu.loc4j.Localization;
import ro.romanescu.loc4j.LocalizationException;
import ro.romanescu.loc4j.xliff.Xliff;

/**
 * This class manages the library within the framework.
 * Keeps tracks of the dictionaries and provide access to them.
 */
public class LocalizationImpl extends HashMap<String, Dictionary> implements Localization {
  private static final Log log = LogFactory.getLog( LocalizationImpl.class );
  private boolean lazyLoading = true;


  /**
   * Configure the library from the given configuration file.
   * @param configurationFile Path to the configuration file
   * @throws LocalizationException If data is not correctly defined
   */
  public void configure( URL configurationFile ) throws LocalizationException {
    XMLInputFactory2 xmlif = null;
    try {
      xmlif = ( XMLInputFactory2 )XMLInputFactory.newInstance();
      xmlif.setProperty( XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, Boolean.FALSE );
      xmlif.setProperty( XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE );
      xmlif.setProperty( XMLInputFactory.IS_COALESCING, Boolean.FALSE );
      xmlif.configureForSpeed();

      XMLStreamReader2 xmlr = xmlif.createXMLStreamReader( configurationFile );

      int evtType;
      String currentElement;
      while ( xmlr.next() != XMLEvent.END_DOCUMENT ) {
        evtType = xmlr.getEventType();
        switch ( evtType ) {
        case XMLEvent.START_ELEMENT:
          currentElement = xmlr.getName().toString();
          if ( currentElement.equals( "loc4j" ) ) {
            int cnt = xmlr.getAttributeCount();
            for( int i = 0; i < cnt; i++ ) {
              String attrName = xmlr.getAttributeName( i ).toString();
              if( attrName.equals( "lazyLoading" ) ) {
                String attrValue = xmlr.getAttributeValue( i );
                lazyLoading = Boolean.parseBoolean( attrValue );
              }
            }
            log.debug( "loc4j configuration section detected" );
            if( lazyLoading ) {
              log.info( "Dictionary data will be loaded lazily on first access" );
            }
            readFileElements( xmlr );
          }
        }
      }
    } catch ( XMLStreamException ex ) {
      ex.printStackTrace();
    }
  }


  private void readFileElements( XMLStreamReader2 xmlr ) throws LocalizationException {
    int evtType;
    String currentElement = "";
    try {
      while ( !currentElement.equals( "loc4j" ) ) {
        evtType = xmlr.getEventType();
        switch ( evtType ) {
        case XMLEvent.START_ELEMENT:
          if ( currentElement.equals( "dictionary" ) ) {
            int attrCount = xmlr.getAttributeCount();
            Dictionary dic = new Xliff();
            dic.setLazyLoading( lazyLoading );
            for ( int i = 0; i < attrCount; i++ ) {
              String attrName = xmlr.getAttributeName( i ).toString();
              String attrVal = xmlr.getAttributeValue( i );
              if ( attrName.equals( "path" ) ) {
                dic.setXliff( new URL( "file", null, 0, attrVal ) );
              }
              if ( attrName.equals( "languageISOCode" ) ) {
                dic.setLanguageISOCode( attrVal );
              }
              if ( attrName.equals( "languageName" ) ) {
                dic.setLanguageName( attrVal );
              }
              if ( attrName.equals( "languageNameNative" ) ) {
                dic.setLanguageNameNative( attrVal );
              }
            }
            put( dic.getLanguageISOCode(), dic );
            log.info( "Loaded dictionary " + dic );
          }
          break;
        case XMLEvent.END_ELEMENT:
          currentElement = xmlr.getName().toString();
          if ( currentElement.equals( "loc4j" ) )
            return;
        }
        xmlr.nextTag();
        currentElement = xmlr.getName().toString();
      }
    } catch ( MalformedURLException ex ) {
      throw new LocalizationException( ex );
      
    } catch (XMLStreamException ex ) {
      throw new LocalizationException( ex );
    }

  }


  /**
   * Retrieve dictionary for a specified language code.
   * @param languageCode Language ISO code
   * @return Dictionary or null if no such code is loaded
   */
  public Dictionary getDictionary( String languageCode ) {
    if ( containsKey( languageCode ) ) {
      return get( languageCode );
    }
    log.warn( String.format( "Missing dictionary for language with ISO code: %s", languageCode ) );
    return null;
  }


  /**
   * Retrieve the list of loaded dictionaries.
   * @return List of dictionaries loaded by the framework
   */
  public Dictionary[] getDictionaries() {
    return values().toArray( new Dictionary[ size() ] );
  }


  /**
   * Retrieve the list of loaded dictionaries.
   * @return List of dictionaries loaded by the framework
   */
  public boolean isLazyLoading() {
    return lazyLoading;
  }
}
